-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
--
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.
local mc_utils = require 'mc.utils'

local m = {}
local IPV4_PATH<const> = '/bmc/kepler/Managers/1/EthernetInterfaces/Ipv4'
local IPV4_INTERFACE<const> = 'bmc.kepler.Managers.EthernetInterfaces.Ipv4'
local ETHERNET_PATH<const> = '/bmc/kepler/Managers/1/EthernetInterfaces'
local ETHERNET_INTERFACE<const> = 'bmc.kepler.Managers.EthernetInterfaces'
local MGMT_PORT_INTERFACE<const> =
    'bmc.kepler.Managers.EthernetInterfaces.MgmtPort'
local ETH_PORT_INTERFACE<const> = 'bmc.kepler.Managers.EthernetInterfaces.MgmtPort'
local ETH_GROUP_INTERFACE<const> = 'bmc.kepler.Managers.EthernetInterfaces.EthGroup'
local DEDICATED_TYPE<const> = 'Dedicated'

-- 获取NCSI网口端口号
local function get_eth_host_port()
    local eth_obj = mdb.get_object(bus, ETHERNET_PATH, ETHERNET_INTERFACE)
    local active_port = mdb.get_object(bus, ETHERNET_PATH .. '/' .. eth_obj.PortId, MGMT_PORT_INTERFACE)
    -- 如果当前是专用口，固定返回2，和v2保持一致
    if active_port.Type == DEDICATED_TYPE then
        return 2
    end
    -- NCSI口返回端口号
    return active_port.DevicePortId + 1 -- 返回时需加1，snmp回显时会减掉1
end

local function get_ethernet_group_info(i, ethGroup_obj)
    local active_port_id = ethGroup_obj.ActivePortId
    local port_path = ETHERNET_PATH .. "/" .. active_port_id
    local ok, port_obj = pcall(mdb.get_object, bus, port_path, ETH_PORT_INTERFACE)
    if not ok then
       return
    end

    local eth_group_mode = 0
    local eth_group_mode_table = {LOM = 2, Dedicated = 1, ExternalPCIe = 4}
    if ethGroup_obj.NetMode == 'Automatic' then
        eth_group_mode = 3
    else
        if eth_group_mode_table[port_obj.Type] ~= nil then
            eth_group_mode = eth_group_mode_table[port_obj.Type]
        end
    end
    local vlan_id = ''
    if not ethGroup_obj.VLANEnabled then
        vlan_id = 'off'
    else
        vlan_id = tostring(ethGroup_obj.VLANId)
    end
    local eth_group_info = string.format('%s,%s,%s', ethGroup_obj.IpAddr, ethGroup_obj.SubnetMask,
                                   ethGroup_obj.DefaultGateway)
    local ipv4_enable = ethGroup_obj.IpVersion == 'IPv6' and 1 or 2
    local ethGroup_host_port = 0
    if port_obj.Type == DEDICATED_TYPE then
        ethGroup_host_port = 2
    else
        ethGroup_host_port = port_obj.DevicePortId + 1 -- 返回时需加1，snmp回显时会减掉1
    end

    local ethernet_group_info = {
        ethNum = i,
        ethIPAddress = ethGroup_obj.IpAddr,
        ethNetmask = ethGroup_obj.SubnetMask,
        ethDefaultGateway = ethGroup_obj.DefaultGateway,
        ethIPSource = ethGroup_obj.IpMode == 'Static' and 1 or 2,
        ethMACAddress = ethGroup_obj.Mac,
        ethType = port_obj.Type == DEDICATED_TYPE and 1 or 2, -- 1表示mgmt，2表示share
        ethHostPort = ethGroup_host_port,
        ethEnable = 2,
        ethMode = eth_group_mode,
        vlanID = vlan_id,
        ethInfo = eth_group_info,
        ethIPv4Enable = ipv4_enable
    }
    ethernet_group_info['@Instance'] = {ethernet_group_info.ethNum + 1}
    return ethernet_group_info
end

local function insert_eth_group_info(info, group_paths)
    local networkInfos = {info}
    local ethGroupInfos = {}
    local ethGroup_objs = {}
    for _, group_path in ipairs(group_paths) do
        local ok, ethGroup_obj = pcall(mdb.get_object, bus, group_path, ETH_GROUP_INTERFACE)
        if ok and ethGroup_obj.OutType == 2 and ethGroup_obj.Status then
            table.insert(ethGroup_objs, ethGroup_obj)
        end
    end

    if ethGroup_objs ~= nil then
        table.sort(ethGroup_objs, function(a, b)
            return a.GroupId < b.GroupId
        end)
        for i, ethGroup_obj in ipairs(ethGroup_objs) do
        local ethernet_group_info = get_ethernet_group_info(i, ethGroup_obj)
            table.insert(ethGroupInfos, ethernet_group_info)
        end
    end

    for _, ethGroupInfo in ipairs(ethGroupInfos) do
        table.insert(networkInfos, ethGroupInfo)
    end

    return networkInfos
end

function m.get_network_rsp(group_paths)
    local ipv4_obj = mdb.get_object(bus, IPV4_PATH, IPV4_INTERFACE)
    local ipv4_addr = ipv4_obj.IpAddr
    local submask = ipv4_obj.SubnetMask
    local default_gateway = ipv4_obj.DefaultGateway
    local ip_source = ipv4_obj.IpMode == 'Static' and 1 or 2
    local eth_obj = mdb.get_object(bus, ETHERNET_PATH, ETHERNET_INTERFACE)
    local mac = eth_obj.Mac
    local port = mdb.get_object(bus, ETHERNET_PATH .. '/' .. eth_obj.PortId,
                                MGMT_PORT_INTERFACE)
    local eth_mode = 0
    local eth_mode_table = {LOM = 2, Dedicated = 1, ExternalPCIe = 4}
    if eth_obj.NetMode == 'Automatic' then
        eth_mode = 3
    else
        if eth_mode_table[port.Type] ~= nil then
            eth_mode = eth_mode_table[port.Type]
        end
    end
    local vlan_id = ''
    if not eth_obj.VLANEnable then
        vlan_id = 'off'
    else
        vlan_id = tostring(eth_obj.VLANId)
    end
    local eth_info = string.format('%s,%s,%s', ipv4_addr, submask,
                                   default_gateway)
    local ipv4_enable = eth_obj.IpVersion == 'IPv6' and 1 or 2
    local info = {
        ethNum = 0,
        ethIPAddress = ipv4_addr,
        ethNetmask = submask,
        ethDefaultGateway = default_gateway,
        ethIPSource = ip_source,
        ethMACAddress = mac,
        ethType = port.Type == DEDICATED_TYPE and 1 or 2, -- 1表示mgmt，2表示share
        ethHostPort = get_eth_host_port(),
        ethEnable = 2,
        ethMode = eth_mode,
        vlanID = vlan_id,
        ethInfo = eth_info,
        ethIPv4Enable = ipv4_enable
    }
    info['@Instance'] = {info.ethNum + 1}

    return insert_eth_group_info(info, group_paths)
end

local function get_port_id_by_type(type)
    local port_id = nil
    local ports = mdb.get_sub_objects(bus, ETHERNET_PATH, MGMT_PORT_INTERFACE)
    for _, value in pairs(ports) do
        -- 默认切到port1和v2保持一致
        if value.Type == type and value.DevicePortId == 1 then
            return value.Id
        end
    end
    return port_id
end

function m.get_net_mode(eth_mode)
    local eth_obj = mdb.get_object(bus, ETHERNET_PATH, ETHERNET_INTERFACE)
    -- 3表示自适应模式
    if eth_mode == 3 then
        -- 如果当前为自适应模式不作处理
        if eth_obj.NetMode == 'Automatic' then return {SetFlag = false} end
        return {
            SetFlag = true,
            NetMode = 'Automatic',
            PortId = eth_obj.PortId,
            VLANEnable = eth_obj.VLANEnable,
            VLANId = eth_obj.VLANId
        }
    end
    local port = mdb.get_object(bus, ETHERNET_PATH .. '/' .. eth_obj.PortId,
                                MGMT_PORT_INTERFACE)
    local port_id = nil
    if eth_mode == 2 and port.Type ~= 'LOM ' then
        port_id = get_port_id_by_type('LOM')
    elseif eth_mode == 1 and port.Type ~= 'Dedicated' then
        port_id = 1
    elseif eth_mode == 4 and port.Type ~= 'ExternalPCIe' then
        port_id = get_port_id_by_type('ExternalPCIe')
    end
    if port_id == nil then port_id = eth_obj.PortId end
    return {
        SetFlag = true,
        NetMode = 'Fixed',
        PortId = port_id,
        VLANEnable = eth_obj.VLANEnable,
        VLANId = eth_obj.VLANId
    }
end

function m.get_ipv4_info(eth_info)
    local info = mc_utils.split(eth_info, ',')
    return {IpAddr = info[1], SubnetMask = info[2], DefaultGateway = info[3]}
end

function m.get_ip_version(ipv4_enable)
    local ip_version = mdb.get_object(bus, ETHERNET_PATH, ETHERNET_INTERFACE)
                           .IpVersion
    if ip_version == 'IPv4' and ipv4_enable == 1 then
        error(custom_messages.OperationNotAllowed())
    end
    if ip_version == 'IPv6' and ipv4_enable == 2 then return 'IPv4AndIPv6' end
    if ip_version == 'IPv4AndIPv6' and ipv4_enable == 1 then return 'IPv6' end
    return ip_version
end

-- 根据传入的NCSI端口号获取要切换的网口
function m.get_port_id_by_host_port(host_port)
    local eth_obj = mdb.get_object(bus, ETHERNET_PATH, ETHERNET_INTERFACE)
    local active_port = mdb.get_object(bus, ETHERNET_PATH  .. '/' .. eth_obj.PortId, MGMT_PORT_INTERFACE)
    local active_port_type = active_port.Type
    local port_id = nil
    local ports = mdb.get_sub_objects(bus, ETHERNET_PATH, MGMT_PORT_INTERFACE)
    for _, value in pairs(ports) do
        if value.Type == active_port_type and value.DevicePortId == host_port - 1 then
            port_id = value.Id
        end
    end
    if port_id == nil then
        custom_messages.PortNotExist(active_port_type, host_port - 1)
    end
    return port_id
end

return m
